package br.com.mayco.persistence;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;

/**
 * @author Mayco Lucian
 * @since 25/02/2013
 * version 1.0.0
 */

public abstract class BaseDao<T extends Serializable> implements Serializable{

    private Class entityClass;

    public BaseDao(Class clazz) {
        entityClass = clazz;
    }
    public Class getEntityClass() {
        return entityClass;
    }
    public void setEntityClass(Class entityClass) {
        this.entityClass = entityClass;
    }
    public boolean execute(String method, T obj) {
        EntityManager em = Database.getDb().createEntityManager();
        try {
            em.getTransaction().begin();
            Method m = EntityManager.class.getMethod(method, Object.class);
            if (method.equals(DELETE)) {
                obj = (T) em.getReference(entityClass, new EntityDecorator(obj).getPrimaryKey());
            }
            m.invoke(em, obj);
            em.getTransaction().commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            em.close();
        }
    }
    public boolean create(T obj) {
        return execute(CREATE, obj);
    }
    public boolean update(T obj) {
        return execute(UPDATE, obj);
    }
    public boolean delete(T obj) {
        return execute(DELETE, obj);
    }
    public Collection<T> getAll() {
        EntityManager em = Database.getDb().createEntityManager();
        try {
            em.getTransaction().begin();
            T obj = (T) entityClass.newInstance();
            Collection<T> lst = em.createNamedQuery(new EntityDecorator(obj).getSelect()).getResultList();
            em.getTransaction().commit();
            return lst;
        } catch (Exception e) {
            e.printStackTrace();
            em.getTransaction().rollback();
            return new ArrayList<T>();
        } finally {
            em.close();
        }
    }
    public T getByPrimaryKey(Object key) {
        EntityManager em = Database.getDb().createEntityManager();
        try {
            em.getTransaction().begin();
            T obj = (T) em.find(entityClass, key);
            em.getTransaction().commit();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            em.close();
        }
    }
    public Collection<T> getByNamedQuery(String query, QueryParameter... values) {
        EntityManager em = Database.getDb().createEntityManager();
        try {
            em.getTransaction().begin();
            Query nq = em.createNamedQuery(query);
            for (int i = 0; i<values.length; i++)
               nq.setParameter(values[i].getField(), values[i].getValue());
            Collection<T> lst = nq.getResultList();
            em.getTransaction().commit();
            return lst;
        } catch (Exception e) {
            e.printStackTrace();
            em.getTransaction().rollback();
            return new ArrayList<T>();
        } finally {
            em.close();
        }
    }

    public T getFirstByNamedQuery(String query, QueryParameter... values){
        List<T> byNamedQuery = (List<T>) getByNamedQuery(query, values);
        if (byNamedQuery.size()>0)
            return byNamedQuery.get(0);
        return null;
    }

    public Collection<T> getByQuery(String query) {
        EntityManager em = Database.getDb().createEntityManager();
        try {
            em.getTransaction().begin();
            Query nq = em.createNativeQuery(query, entityClass);
            Collection<T> lst = nq.getResultList();
            em.getTransaction().commit();
            return lst;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<T>();
        } finally {
            em.close();
        }
    }

    public static final String CREATE = "persist";
    public static final String UPDATE = "merge";
    public static final String DELETE = "remove";
}